package org.tity.commons.network.webservice;

import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.cxf.endpoint.Client;
import org.apache.cxf.endpoint.Endpoint;
import org.apache.cxf.jaxws.endpoint.dynamic.JaxWsDynamicClientFactory;
import org.apache.cxf.service.model.BindingMessageInfo;
import org.apache.cxf.service.model.BindingOperationInfo;
import org.apache.cxf.service.model.MessagePartInfo;
import org.tity.commons.log.Logger;

import com.alibaba.fastjson.JSONObject;
import com.esotericsoftware.reflectasm.FieldAccess;
 
/**
 * @author 李方捷
 * */
class CXFClient extends WebServiceClient {
	
	

	private JaxWsDynamicClientFactory factory=null;
	private Client client=null;
	private Endpoint endpoint = null;

	public CXFClient(String wsdl)
	{
		super(wsdl);
		this.factory=JaxWsDynamicClientFactory.newInstance();
		this.client=factory.createClient(this.getWsdl());
		this.endpoint=client.getEndpoint();
		initWSDL();
	}
	
	private HashMap<String,BindingOperationInfo> operators = new HashMap<String,BindingOperationInfo>();
	private HashMap<String,List<Field>> operatorParamTypes = new HashMap<String,List<Field>>();
 
	/**
	 * 初始化
	 * */
	private void initWSDL() {

		Collection<BindingOperationInfo> ops = endpoint.getEndpointInfo().getBinding().getOperations();
		int i = 0;
		for (BindingOperationInfo boi : ops) {
			// 不考虑对象方法的重载问题
			operators.put(boi.getName().getLocalPart(), boi);
			ArrayList<Field> mFields=new ArrayList<Field>();
			operatorParamTypes.put(boi.getName().getLocalPart(), mFields);
			System.out.println("方法[" + i + "] : " + boi.getName().getLocalPart());
			i++;
			BindingMessageInfo inputMessageInfo = boi.getInput();
			List<MessagePartInfo> parts = inputMessageInfo.getMessageParts();
			for (MessagePartInfo p : parts) {
				Class<?> partClass = p.getTypeClass();
				try {
					Field[] fields = partClass.getDeclaredFields();
					int j = 0;
					for (Field f : fields) {
						mFields.add(f);
						System.out.println("\t参数[" + j + "] : " + f.getType().getName());
						j++;
					}
				} catch (Exception e) {
					Logger.exception(e);
				}
			}

		}
	}

    /**
     *  调用WebService方法<br>
     *  如果参数为常规数值类型，则直接传值；如果参数为pojo类型则可以使用对用的Pojo类型或Map类型，方法内自动转换成WebService需要的类型
     * @param  methodName 方法名
     * @param params  参数列表
     * @return  返回结果
     * */
    public JSONObject invoke(String methodName,Object... params)
    {
    	BindingOperationInfo boi=operators.get(methodName);
    	if(boi==null)
    	{
    		throw new  IllegalArgumentException(methodName+"方法不存在");
    	}
    	
    	List<Field> paramFields=operatorParamTypes.get(methodName);
    
    	if(params.length!=paramFields.size())
    	{
    		throw new  IllegalArgumentException(methodName+"方法参数数量错误,要求"+paramFields.size()+",实际"+params.length);
    	}
 
    	for (int i = 0; i < params.length; i++) {
    		Object param=params[i];
    		Field field=paramFields.get(i);
    		Class type=field.getType();
    		params[i]=parseParam(type,params[i]);
		}
 
    	try {
			Object[] result=client.invoke(methodName, params);
			if(result==null || result.length==0) return null;
			return  JSONObject.parseObject(JSONObject.toJSONString(result[0]));
//			return JSONObject.toJSONresult[0];
		} catch (Exception e) {
			Logger.exception(e);
			return null;
		}
    	
    }

    /**
     * 进行数据转换
     * */
	private Object parseParam(Class type, Object value) {
		if(value==null) return null;
		if(type.equals(value.getClass())) return value;
		Object obj = null;
		try {
			obj = Thread.currentThread().getContextClassLoader().loadClass(type.getName()).newInstance(); 
		} catch (Exception e) {
			 Logger.exception(e);
		}
		if(obj==null) return null;
 
		 FieldAccess fieldAccess = FieldAccess.get(type);
		 String[] fns=fieldAccess.getFieldNames();
		 
		 FieldAccess valueFieldAccess =null;
		 if(!(value instanceof Map))
		 {
			 valueFieldAccess = FieldAccess.get(value.getClass());
		 }
		
 
		for (String fn : fns) {
 
			Object v = null;
			try {
				v=getPropertyValue(value,fn,valueFieldAccess);
				fieldAccess.set(obj, fn, v);
			} catch (Exception e) {
				Logger.exception(e);
			}
		}
		return obj;
	}
    
	private Object getPropertyValue(Object value,String field, FieldAccess valueFieldAccess) throws Exception
	{
		if(value instanceof Map)
		{
			Map map=(Map)value;
			return map.get(field);
		}
		else
		{
		
		   PropertyDescriptor partPropertyDescriptor = new PropertyDescriptor(field, value.getClass());
		   Method getter = partPropertyDescriptor.getReadMethod();
		   return getter.invoke(value);

		}
	}

	
}
